#include "../../Base/stdafx.h"
#include "../../Base/GameEngine.h"
#include "../../Base/DXDevice.h"
#include "Terrain.h"
#include "../../Base/Vertex.h"
#include "../../PhysX/PhysX.h"
#include "../../Effects/EffectLib.h"
#include "IniReader.h"

const float EPSILON  = 0.001f;

struct SubGrid
{
	const static int NUM_ROWS  = 33;
	const static int NUM_COLS  = 33;
	const static int NUM_TRIS  = (NUM_ROWS-1)*(NUM_COLS-1)*2;
	const static int NUM_VERTS = NUM_ROWS*NUM_COLS;
};
Terrain::Terrain(const tstring& iniFilename): m_Layout(0), m_Effect(0), m_HeightMap(0),m_NumBits(8)
{
	LOG(tstring(_T("Create Terrain")));
	tstringstream stream;
	IniReader iReader(iniFilename);
	IniFile *pIniFile = iReader.GetIniFile();
	tstring str = pIniFile->GetValue(_T("Terrain"),_T("NumRows"));
	stream<<str;
	stream>>m_VertRows;
	stream.clear();
	str = pIniFile->GetValue(_T("Terrain"),_T("NumColums"));
	stream<<str;
	stream>>m_VertCols;
	stream.clear();
	str = pIniFile->GetValue(_T("Terrain"),_T("NumBits"));
	stream<<str;
	stream>>m_NumBits;
	if(m_NumBits!=16)m_NumBits=8;
	stream.clear();
	str = pIniFile->GetValue(_T("Terrain"),_T("ScaleX"));
	stream<<str;
	stream>>m_ScaleX;
	stream.clear();
	str = pIniFile->GetValue(_T("Terrain"),_T("ScaleY"));
	stream<<str;
	stream>>m_ScaleY;
	stream.clear();
	str = pIniFile->GetValue(_T("Terrain"),_T("ScaleZ"));
	stream<<str;
	stream>>m_ScaleZ;
	stream.clear();
	str = pIniFile->GetValue(_T("Terrain"),_T("OffsetY"));
	float HeightOffset;
	stream<<str;
	stream>>HeightOffset;
	stream.clear();
	tstring FileName_HeightMap, tex0,  tex1,  tex2,  blendMap;
	FileName_HeightMap = pIniFile->GetValue(_T("Terrain"),_T("HeightMap"));
	stream.clear();
	tex0 = pIniFile->GetValue(_T("Terrain"),_T("Tex1"));
	stream.clear();
	tex1 = pIniFile->GetValue(_T("Terrain"),_T("Tex2"));
	stream.clear();
	tex2 = pIniFile->GetValue(_T("Terrain"),_T("Tex3"));
	stream.clear();
	blendMap = pIniFile->GetValue(_T("Terrain"),_T("Blend"));
	stream.clear();

	m_WidthInUnits = (m_VertCols-1)*m_ScaleX;
	m_DepthInUnits = (m_VertRows-1)*m_ScaleZ;

	Init( FileName_HeightMap,  tex0,  tex1, tex2,  blendMap,  m_ScaleY, HeightOffset);
	InitPhysX();

	LOG(tstring(_T("Done Terrain")));

}
Terrain::Terrain(UINT vertRows, UINT vertCols, float scalex, float scalez, const tstring& FileName_HeightMap, 
				 const tstring& tex0, const tstring& tex1, const tstring& tex2, const tstring& blendMap, 
				 float heightScale, float HeightOffset): m_VertRows(vertRows), m_VertCols(vertCols), m_ScaleX(scalex),m_ScaleY(heightScale),m_ScaleZ(scalez),
				 m_Layout(0)
{
	LOG(tstring(_T("Create Terrain")));
	m_WidthInUnits = (m_VertCols-1)*m_ScaleX;
	m_DepthInUnits = (m_VertRows-1)*m_ScaleZ;

	Init( FileName_HeightMap,  tex0,  tex1, tex2,  blendMap,  heightScale, HeightOffset);

	InitPhysX();
	
	LOG(tstring(_T("Done Terrain")));
}

Terrain::~Terrain(void)
{
	Cleanup();
}
void Terrain::Init(const tstring& FileName_HeightMap, const tstring& tex0, const tstring& tex1, const tstring& tex2, const tstring& blendMap, float heightScale, 
				   float HeightOffset)
{
	LoadShader();
	BuildVertexDeclaration();

	//load textures
	D3DX10CreateShaderResourceViewFromFile(DXDEVICE->GetDevice(), tex0.c_str(), NULL, NULL, &m_Texture, NULL);
	D3DX10CreateShaderResourceViewFromFile(DXDEVICE->GetDevice(), tex1.c_str(), NULL, NULL, &m_Texture2, NULL);
	D3DX10CreateShaderResourceViewFromFile(DXDEVICE->GetDevice(), tex2.c_str(), NULL, NULL, &m_Texture3, NULL);
	D3DX10CreateShaderResourceViewFromFile(DXDEVICE->GetDevice(), blendMap.c_str(), NULL, NULL, &m_Blend, NULL);
	//set textures to shader
	m_TextureVar->SetResource(m_Texture);
	m_Texture2Var->SetResource(m_Texture2);
	m_Texture3Var->SetResource(m_Texture3);
	m_BlendVar->SetResource(m_Blend);

	//load raw data file, keep height info in std::vector 
	m_HeightMap = new Terrain_Heightmap();
	if(m_NumBits==16)m_HeightMap->LoadRAW16(m_VertRows, m_VertCols, FileName_HeightMap, heightScale, HeightOffset);
	if(m_NumBits==8)m_HeightMap->LoadRAW(m_VertRows, m_VertCols, FileName_HeightMap, heightScale, HeightOffset);


	BuildGridGeometry();

}
void Terrain::BuildGridGeometry()
{
	//===============================================================
	// Create one large mesh for the grid in system memory.

	DWORD numTris  = (m_VertRows-1)*(m_VertCols-1)*2;
	DWORD numVerts = m_VertRows*m_VertCols;

	//===============================================================
	// Write the grid vertices and triangles to the mesh.

	vector<VertexPNT> v;

	vector<DWORD> vecIndices;
	vector<D3DXVECTOR3> vecVertices;

	GenTriGrid(m_VertRows, m_VertCols, m_ScaleX, m_ScaleZ, D3DXVECTOR3(0.0f, 0.0f, 0.0f), vecVertices, vecIndices);

	float w = m_WidthInUnits;
	float d = m_DepthInUnits;
	for(UINT i = 0; i < numVerts; ++i)
	{
		VertexPNT temp;
		// We store the grid vertices in a linear array, but we can
		// convert the linear array index to an (r, c) matrix index.
		int row = i / m_VertCols;
		int col = i % m_VertCols;

		temp.pos   = vecVertices[i];
		temp.pos.y = m_HeightMap->GetHeight(row, col);

		temp.tex.x = (temp.pos.x + (0.5f*w)) / w;
		temp.tex.y = (temp.pos.z - (0.5f*d)) / -d;
		v.push_back(temp);
	}

	D3D10_BUFFER_DESC desc;
	desc.ByteWidth = sizeof(VertexPNT) * numVerts;
	desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	desc.CPUAccessFlags = 0;
	desc.MiscFlags = 0;
	desc.Usage = D3D10_USAGE_DEFAULT;
	D3D10_SUBRESOURCE_DATA ini;
	ini.pSysMem = &v[0];
	HR(DXDEVICE->GetDevice()->CreateBuffer(&desc, &ini, &m_VertexBuffer));

	// Write triangle data so we can compute normals.

	vector<DWORD> indexBuffPtr;
	for(UINT i = 0; i < numTris; ++i)
	{
		
		indexBuffPtr.push_back(vecIndices[i*3+0]);
		indexBuffPtr.push_back(vecIndices[i*3+1]);
		indexBuffPtr.push_back(vecIndices[i*3+2]);
	}

	D3D10_BUFFER_DESC idesc;
	idesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
	idesc.ByteWidth = sizeof(DWORD) * numTris * 3;
	idesc.CPUAccessFlags = 0;
	idesc.MiscFlags = 0;
	idesc.Usage = D3D10_USAGE_DEFAULT;
	D3D10_SUBRESOURCE_DATA indexbuf;
	indexbuf.pSysMem = &indexBuffPtr[0];

	HR(DXDEVICE->GetDevice()->CreateBuffer(&idesc, &indexbuf, &m_IndexBuffer));

	// Compute Vertex Normals.
	//HR(D3DXComputeNormals(mesh, 0));


	//===============================================================
	// Now break the grid up into subgrid meshes.

	// Find out the number of subgrids we'll have.  For example, if
	// m = 513, n = 257, SUBGRID_VERT_ROWS = SUBGRID_VERT_COLS = 33,
	// then subGridRows = 512/32 = 16 and sibGridCols = 256/32 = 8.
	//int subGridRows = (m_VertRows-1) / (SubGrid::NUM_ROWS-1);
	//int subGridCols = (m_VertCols-1) / (SubGrid::NUM_COLS-1);

	//for(int r = 0; r < subGridRows; ++r)
	//{
	//	for(int c = 0; c < subGridCols; ++c)
	//	{
	//		// Rectangle that indicates (via matrix indices ij) the
	//		// portion of grid vertices to use for this subgrid.
	//		RECT R = 
	//		{
	//			c * (SubGrid::NUM_COLS-1),
	//			r * (SubGrid::NUM_ROWS-1),
	//			(c+1) * (SubGrid::NUM_COLS-1),
	//			(r+1) * (SubGrid::NUM_ROWS-1)
	//		};

	//		buildSubGridMesh(R, v); 
	//	}
	//}
}


void Terrain::buildSubGridMesh(RECT& R, VertexPNT* gridVerts)
{
	//===============================================================
	// Get indices for subgrid (we don't use the m_vecVertices here--the m_vecVertices
	// are given by the parameter gridVerts).

	std::vector<D3DXVECTOR3> tempVerts;
	std::vector<DWORD> tempIndices;
	GenTriGrid(SubGrid::NUM_ROWS, SubGrid::NUM_COLS, m_ScaleX, m_ScaleZ, 
		D3DXVECTOR3(0.0f, 0.0f, 0.0f), tempVerts, tempIndices);

	//===============================================================
	// Build Vertex Buffer.  Copy rectangle of vertices from the
	// grid into the subgrid structure.
	VertexPNT* v = 0;
	int k = 0;
	for(int i = R.top; i <= R.bottom; ++i)
	{
		for(int j = R.left; j <= R.right; ++j)
		{
			v[k++] = gridVerts[i*m_VertCols+j];
		}
	}

	//===============================================================
	// Compute the bounding box before unlocking the vertex buffer.
	AABB bndBox;
	HR(D3DXComputeBoundingBox((D3DXVECTOR3*)v, tempVerts.size(), 
		sizeof(VertexPNT), &bndBox.minPt, &bndBox.maxPt));

	//===============================================================
	// Build Index and Attribute Buffer.
	WORD* indices  = 0;
	DWORD* attBuff = 0;

	for(int i = 0; i < SubGrid::NUM_TRIS; ++i)
	{
		indices[i*3+0] = (WORD)tempIndices[i*3+0];
		indices[i*3+1] = (WORD)tempIndices[i*3+1];
		indices[i*3+2] = (WORD)tempIndices[i*3+2];

		attBuff[i] = 0; // All in subset 0.
	}

	//===============================================================
	// Save the mesh and bounding box.
	m_SubGridBndBoxes.push_back(bndBox);
}

void Terrain::GenTriGrid(int numVertRows, int numVertCols,
						 float scalex, float scalez, 
						 const D3DXVECTOR3& center, 
						 std::vector<D3DXVECTOR3>& m_vecVertices,
						 std::vector<DWORD>& indices)
{
	int numVertices = numVertRows*numVertCols;
	int numCellRows = numVertRows-1;
	int numCellCols = numVertCols-1;

	int numTris = numCellRows*numCellCols*2;

	float width = (float)numCellCols * scalex;
	float depth = (float)numCellRows * scalez;

	//===========================================
	// Build vertices.

	// We first build the grid geometry centered about the origin and on
	// the xz-plane, row-by-row and in a top-down fashion.  We then translate
	// the grid vertices so that they are centered about the specified 
	// parameter 'center'.

	m_vecVertices.resize( numVertices );

	// Offsets to translate grid from quadrant 4 to center of 
	// coordinate system.
	float xOffset = -width * 0.5f; 
	float zOffset =  depth * 0.5f;

	int k = 0;
	for(float row = 0; row < numVertRows; ++row)
	{
		for(float col = 0; col < numVertCols; ++col)
		{
			// Negate the depth coordinate to put in quadrant four.  
			// Then offset to center about coordinate system.
			m_vecVertices[k].x =  col * scalex + xOffset;
			m_vecVertices[k].z = -row * scalez + zOffset;
			m_vecVertices[k].y =  0.0f;

			// Translate so that the center of the grid is at the
			// specified 'center' parameter.
			D3DXMATRIX T;
			D3DXMatrixTranslation(&T, center.x, center.y, center.z);
			D3DXVec3TransformCoord(&m_vecVertices[k], &m_vecVertices[k], &T);

			++k; // Next vertex
		}
	}

	//===========================================
	// Build indices.

	indices.resize(numTris * 3);

	// Generate indices for each quad.
	k = 0;
	for(DWORD i = 0; i < (DWORD)numCellRows; ++i)
	{
		for(DWORD col = 0; col < (DWORD)numCellCols; ++col)
		{
			indices[k]     =   i   * numVertCols + col;
			indices[k + 1] =   i   * numVertCols + col + 1;
			indices[k + 2] = (i+1) * numVertCols + col;

			indices[k + 3] = (i+1) * numVertCols + col;
			indices[k + 4] =   i   * numVertCols + col + 1;
			indices[k + 5] = (i+1) * numVertCols + col + 1;

			// next quad
			k += 6;
		}
	}
}

void Terrain::Draw()
{
	//First calculate final world (includingparent worlds transforms
	this->GetTotalTranspose();
	DXDEVICE->GetMatrixen(m_View, m_Proj);

	m_TextureVar->SetResource(m_Texture);
	m_Texture2Var->SetResource(m_Texture2);
	m_Texture3Var->SetResource(m_Texture3);

	// TODO: Sort front to back, and frustum cull subgrids.
	m_WorldVar->SetMatrix((float*)&m_World);
	m_WVPVar->SetMatrix((float*)&(m_World * m_View * m_Proj));
	D3DXMATRIX worldInv;
	D3DXMatrixInverse(&worldInv, 0, &m_World);
	m_WorldInvVar->SetMatrix((float*)&worldInv);
	D3DXMATRIX viewInv;
	D3DXMatrixInverse(&viewInv, 0, &m_View);
	m_ViewInvVar->SetMatrix((float*)&viewInv);

	DXDEVICE->GetDevice()->IASetInputLayout(m_Layout);
	DXDEVICE->GetDevice()->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	DWORD numTris  = (m_VertRows-1)*(m_VertCols-1)*2;

	D3D10_TECHNIQUE_DESC desc;
	m_Tech->GetDesc(&desc);
	for(UINT i=0; i<desc.Passes; ++i)
	{
		UINT stride = sizeof(VertexPNT);
		UINT offset = 0;
		DXDEVICE->GetDevice()->IASetVertexBuffers(0, 1, &m_VertexBuffer, &stride, &offset);
		DXDEVICE->GetDevice()->IASetIndexBuffer(m_IndexBuffer, DXGI_FORMAT_R32_UINT, 0);
		m_Tech->GetPassByIndex(i)->Apply(0);
		DXDEVICE->GetDevice()->DrawIndexed(numTris * 3, 0, 0);
	}
}
float Terrain::GetHeight(float x, float z) const
{
	// Transform from terrain local space to "cell" space.
	float c = (x + 0.5f*m_WidthInUnits) /  m_ScaleX;
	float d = (z - 0.5f*m_DepthInUnits) / -m_ScaleZ;

	// Get the row and column we are in.
	int row = (int)floorf(d);
	int col = (int)floorf(c);


	if(row<0 || row>(int)m_VertRows || col<0 ||col>(int)m_VertCols)
		return -100;//make sure we see the error



	// Grab the heights of the cell we are in.
	// A*--*B
	//  | /|
	//  |/ |
	// C*--*D
	float A = m_HeightMap->GetHeight(row, col);
	float B = m_HeightMap->GetHeight(row, col+1);
	float C = m_HeightMap->GetHeight(row+1, col);
	float D = m_HeightMap->GetHeight(row+1, col+1);

	// Where we are relative to the cell.
	float s = c - (float)col;
	float t = d - (float)row;

	// If upper triangle ABC.
	if(t < 1.0f - s)
	{
		float uy = B - A;
		float vy = C - A;
		return A + s*uy + t*vy;
	}
	else // lower triangle DCB.
	{
		float uy = C - D;
		float vy = B - D;
		return D + (1.0f-s)*uy + (1.0f-t)*vy;
	}
}
void Terrain::InitPhysX()
{
	NxU32 nbColumns=m_VertCols;
	NxU32 nbRows=m_VertRows;

	NxHeightFieldDesc heightFieldDesc;

	heightFieldDesc.nbColumns           = nbColumns;
	heightFieldDesc.nbRows              = nbRows;
	heightFieldDesc.thickness      = -100;
	heightFieldDesc.convexEdgeThreshold = 0;


	// allocate storage for samples
	heightFieldDesc.samples             = new NxU32[nbColumns*nbRows];
	heightFieldDesc.sampleStride        = sizeof(NxU32);

	NxU8* currentByte = (NxU8*)heightFieldDesc.samples;

	for (NxU32 row = 0; row < nbRows; row++)
	{
		for (NxU32 column = 0; column < nbColumns; column++)
		{
			NxHeightFieldSample* currentSample = (NxHeightFieldSample*)currentByte;

			//The rows of the heightfield grid are mapped to the x axis and the columns to the z axis. 
			//Rows and columns increase from the origin in the positive direction.
			//in de FileName_HeightMap is dit omgekeerd!! zowel x enz omwisselen, als de z as spiegelen
			//PhysX gebruikt een hoogte resolutie van 16 bit, onze hoogteinfo is slechts 8 bit
			//Indien de 16 bit niet benut wordt gebruiken we slechts een klein stuk van het bereik. 
			//Het gevolg is dat voorwerpen onnauwkeurig worden berekend. 
			//PRAKTISCH:  een bal zit half in de grond .
			//dus vermenigvuldigen we de float met 256, en later gebruiken we de heightscale om terug de normale hoogte te hebben.
			currentSample->height         = (unsigned short)(256*m_HeightMap->GetHeight(m_VertCols-1-column,row)); //desired height value. Signed 16bit integer
			currentSample->materialIndex0 = 0;
			currentSample->materialIndex1 = 0;

			currentSample->tessFlag = 0;

			currentByte += heightFieldDesc.sampleStride;
		}
	}

	m_HeightField = PhysX::GetSingleton()->GetScene()->getPhysicsSDK().createHeightField(heightFieldDesc);

	// data has been copied, we can free our buffer
	delete[] heightFieldDesc.samples;


	NxHeightFieldShapeDesc heightFieldShapeDesc;
	heightFieldShapeDesc.heightField     = m_HeightField;
	heightFieldShapeDesc.heightScale     = 1/256.0f;//
	heightFieldShapeDesc.rowScale        = m_ScaleX;    
	heightFieldShapeDesc.columnScale     = m_ScaleZ;    
	heightFieldShapeDesc.materialIndexHighBits = 0;    
	heightFieldShapeDesc.holeMaterial = 2;    

	NxActorDesc actorDesc;    
	actorDesc.shapes.pushBack(&heightFieldShapeDesc);    
	NxVec3 pos(-(NxReal)(m_WidthInUnits/2),0,-(NxReal)(m_DepthInUnits/2));
	actorDesc.globalPose.t = pos;    
	m_Actor = PhysX::GetSingleton()->GetScene()->createActor(actorDesc);
}

void Terrain::LoadShader()
{
	m_Effect = EFFECTLIB->CreateEffect(_T("./Shaders/terrain.fx"));
	m_Tech	 = m_Effect->GetTechniqueByIndex(0);
	m_TextureVar = m_Effect->GetVariableBySemantic("TEX1")->AsShaderResource();
	m_Texture2Var = m_Effect->GetVariableBySemantic("TEX2")->AsShaderResource();
	m_Texture3Var = m_Effect->GetVariableBySemantic("TEX3")->AsShaderResource();
	m_BlendVar = m_Effect->GetVariableBySemantic("BLEND")->AsShaderResource();
	m_WVPVar = m_Effect->GetVariableBySemantic("WorldViewProjection")->AsMatrix();
	m_WorldVar = m_Effect->GetVariableBySemantic("World")->AsMatrix();
	m_WorldInvVar = m_Effect->GetVariableBySemantic("WorldInverseTranspose")->AsMatrix();
	m_ViewInvVar = m_Effect->GetVariableBySemantic("ViewInverse")->AsMatrix();

}
void Terrain::Cleanup()
{
	if(m_VertexBuffer) m_VertexBuffer->Release();
	if(m_IndexBuffer) m_IndexBuffer->Release();
	if(m_HeightMap) delete m_HeightMap;
	if(m_Layout) m_Layout->Release();
	if(m_Texture) m_Texture->Release();
	if(m_Texture2) m_Texture2->Release();
	if(m_Texture3) m_Texture3->Release();
	if(m_Blend) m_Blend->Release();
}

void Terrain::BuildVertexDeclaration()
{
	const D3D10_INPUT_ELEMENT_DESC layoutScene[] =
	{
		{ "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD",    0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};
	UINT numElementsScene = sizeof(layoutScene)/sizeof(layoutScene[0]);
	D3D10_PASS_DESC passDesc;
	m_Tech->GetPassByIndex(0)->GetDesc(&passDesc);
	DXDEVICE->GetDevice()->CreateInputLayout(layoutScene, numElementsScene, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize,  &m_Layout);

}